package cn.xhy.module.purchase.service.materialdemand;

import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import cn.xhy.module.purchase.common.MaterialAuditStatusEnum;
import cn.xhy.module.purchase.dal.dataobject.materialdemand.ProjectOrderDO;
import cn.xhy.module.purchase.dal.mysql.materialdemand.ProjectOrderMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.mzt.logapi.context.LogRecordContext;
import com.mzt.logapi.starter.annotation.LogRecord;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

import org.springframework.util.CollectionUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

import cn.xhy.module.purchase.controller.admin.materialdemand.vo.*;
import cn.xhy.module.purchase.dal.dataobject.materialdemand.MaterialDemandDO;
import cn.xhy.module.purchase.dal.dataobject.materialdemand.OfficeOrderDO;

import cn.iocoder.yudao.framework.common.pojo.PageResult;

import cn.iocoder.yudao.framework.common.util.object.BeanUtils;

import cn.xhy.module.purchase.dal.mysql.materialdemand.MaterialDemandMapper;
import cn.xhy.module.purchase.dal.mysql.materialdemand.OfficeOrderMapper;


import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.xhy.module.purchase.enums.ErrorCodeConstants.*;
import static cn.xhy.module.purchase.enums.LogRecordConstants.CRM_RECEIVABLE_SUBMIT_SUB_TYPE;
import static cn.xhy.module.purchase.enums.LogRecordConstants.MATERIAL_DEMAND_TYPE;
import static cn.xhy.module.purchase.enums.LogRecordConstants.CRM_RECEIVABLE_SUBMIT_SUCCESS;
import static cn.xhy.module.purchase.util.MaterialAuditStatusUtils.convertBpmResultToAuditStatus;


/**
 * 单据 Service 实现类
 *
 * @author xhy
 */
@Service
@Validated
@Slf4j
public class MaterialDemandServiceImpl implements MaterialDemandService {

    @Resource
    private MaterialDemandMapper materialDemandMapper;
    @Resource
    private OfficeOrderMapper officeOrderMapper;
    @Resource
    private ProjectOrderMapper projectOrderMapper;

    /**
     * BPM 申请审批流程标识
     */
    public static final String BPM_PROCESS_DEFINITION_KEY = "werp-cg-audit";

    @Resource
    private BpmProcessInstanceApi bpmProcessInstanceApi;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createMaterialDemand(MaterialDemandSaveReqVO createReqVO) {
        // 插入
        MaterialDemandDO materialDemand = BeanUtils.toBean(createReqVO, MaterialDemandDO.class);
        if (!CollectionUtils.isEmpty(createReqVO.getProjectOrders())) {
            List<String> gnames = createReqVO.getProjectOrders().stream()
                    .map(o -> o.getGname())
                    .limit(3)
                    .collect(Collectors.toList());
            String result = String.join(",", gnames);
            materialDemand.setGoodsInfo(result);
        }
        materialDemandMapper.insert(materialDemand);

        createProjectOrderList(materialDemand, createReqVO.getProjectOrders());
        // 返回
        return materialDemand.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateMaterialDemand(MaterialDemandSaveReqVO updateReqVO) {
        // 校验存在
        validateMaterialDemandExists(updateReqVO.getId());
        // 更新
        MaterialDemandDO updateObj = BeanUtils.toBean(updateReqVO, MaterialDemandDO.class);
        materialDemandMapper.updateById(updateObj);

        // 更新子表
//        updateOfficeOrderList(updateReqVO.getDocumentNumber(), updateReqVO.getOfficeOrders());
        updateProjectOrderList(updateObj, updateReqVO.getProjectOrders());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteMaterialDemand(Long id) {
        // 校验存在
        validateMaterialDemandExists(id);
        MaterialDemandDO materialDemandDO = materialDemandMapper.selectOne(new LambdaQueryWrapper<MaterialDemandDO>().eq(MaterialDemandDO::getId, id));
        // 删除子表
        deleteProjectOrderByDocumentNumber(materialDemandDO.getDocumentNumber());
        // 删除
        materialDemandMapper.deleteById(id);
    }

    private void validateMaterialDemandExists(Long id) {
        if (materialDemandMapper.selectById(id) == null) {
            throw exception(MATERIAL_DEMAND_NOT_EXISTS);
        }
    }

    private MaterialDemandDO validateMaterialDemandSpExists(Long id) {
        MaterialDemandDO materialDemandDO = materialDemandMapper.selectById(id);
        if (materialDemandDO == null) {
            throw exception(MATERIAL_DEMAND_NOT_EXISTS);
        }
        return materialDemandDO;
    }

    @Override
    public MaterialDemandDO getMaterialDemand(Long id) {
        return materialDemandMapper.selectById(id);
    }

    @Override
    public PageResult<MaterialDemandDO> getMaterialDemandPage(MaterialDemandPageReqVO pageReqVO) {
        return materialDemandMapper.selectPage(pageReqVO);
    }

    // ==================== 子表（办公申请单） ====================

    @Override
    public List<OfficeOrderDO> getOfficeOrderListByDocumentNumber(String documentNumber) {
        return officeOrderMapper.selectListByDocumentNumber(documentNumber);
    }

    private void createOfficeOrderList(String documentNumber, List<OfficeOrderDO> list) {
        list.forEach(o -> o.setDocumentNumber(documentNumber));
        officeOrderMapper.insertBatch(list);
    }

    private void updateOfficeOrderList(String documentNumber, List<OfficeOrderDO> list) {
        deleteOfficeOrderByDocumentNumber(documentNumber);
        list.forEach(o -> o.setId(null).setUpdater(null).setUpdateTime(null)); // 解决更新情况下：1）id 冲突；2）updateTime 不更新
        createOfficeOrderList(documentNumber, list);
    }

    private void deleteOfficeOrderByDocumentNumber(String documentNumber) {
        officeOrderMapper.deleteByDocumentNumber(documentNumber);
    }

    // ==================== 子表（项目物资申请单） ====================

    @Override
    public List<ProjectOrderDO> getProjectOrderListByDocumentNumber(String documentNumber) {
        return projectOrderMapper.selectListByDocumentNumber(documentNumber);
    }

    @Override
    public void updateByIdMaterialDemand(MaterialDemandSaveReqVO updateReqVO) {
        validateMaterialDemandExists(updateReqVO.getId());
        // 更新
        MaterialDemandDO updateObj = BeanUtils.toBean(updateReqVO, MaterialDemandDO.class);
        materialDemandMapper.updateById(updateObj);
    }

    @Transactional(rollbackFor = Exception.class)
    @LogRecord(type = MATERIAL_DEMAND_TYPE, subType = CRM_RECEIVABLE_SUBMIT_SUB_TYPE, bizNo = "{{#id}}",
            success = CRM_RECEIVABLE_SUBMIT_SUCCESS)
    @Override
    public void submitReceivable(Long id, Long userId) {
        // 1. 校验回款是否在审批
        MaterialDemandDO materialDemandDO = validateMaterialDemandSpExists(id);
        if (ObjUtil.notEqual(materialDemandDO.getAuditStatus(), MaterialAuditStatusEnum.DRAFT.getStatus())) {
            throw exception(RECEIVABLE_SUBMIT_FAIL_NOT_DRAFT);
        }
        // 2. 创建回款审批流程实例
        String processInstanceId = bpmProcessInstanceApi.createProcessInstance(userId, new BpmProcessInstanceCreateReqDTO()
                .setProcessDefinitionKey(BPM_PROCESS_DEFINITION_KEY).setBusinessKey(String.valueOf(id))).getCheckedData();
        // 3. 更新回款工作流编号
        materialDemandMapper.updateById(new MaterialDemandDO().setId(id).setProcessInstanceId(processInstanceId)
                .setAuditStatus(MaterialAuditStatusEnum.PROCESS.getStatus()));
        // 4. 记录日志
        LogRecordContext.putVariable("documentNumber", materialDemandDO.getDocumentNumber());
    }


    @Override
    public void updateContractAuditStatus(Long id, Integer bpmResult) {
        // 1.1 校验合同是否存在
        MaterialDemandDO materialDemandDO = validateMaterialDemandSpExists(id);
        // 1.2 只有审批中，可以更新审批结果
        if (ObjUtil.notEqual(materialDemandDO.getAuditStatus(), MaterialAuditStatusEnum.PROCESS.getStatus())) {
            log.error("[updateContractAuditStatus][contract({}) 不处于审批中，无法更新审批结果({})]",
                    materialDemandDO.getId(), bpmResult);
            throw exception(CONTRACT_UPDATE_AUDIT_STATUS_FAIL_NOT_PROCESS);
        }

        // 2. 更新合同审批结果
        Integer auditStatus = convertBpmResultToAuditStatus(bpmResult);
        materialDemandMapper.updateById(new MaterialDemandDO().setId(id).setAuditStatus(auditStatus));
    }

    private void createProjectOrderList(MaterialDemandDO materialDemand, List<ProjectOrderDO> list) {
        list.forEach(o -> o.setDocumentNumber(materialDemand.getDocumentNumber())
                .setDocumentDate(materialDemand.getDocumentDate()));
        projectOrderMapper.insertBatch(list);
    }

    private void updateProjectOrderList(MaterialDemandDO materialDemand, List<ProjectOrderDO> list) {
        deleteProjectOrderByDocumentNumber(materialDemand.getDocumentNumber());
        list.forEach(o -> o.setId(null).setUpdater(null).setUpdateTime(null)); // 解决更新情况下：1）id 冲突；2）updateTime 不更新
        createProjectOrderList(materialDemand, list);
    }

    private void deleteProjectOrderByDocumentNumber(String documentNumber) {
        projectOrderMapper.deleteByDocumentNumber(documentNumber);
    }

}
